// $Id: array_impl.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#define XPCOM_SOURCE

#ifdef XPCOM_USE_TEMPLATE_ARRAY_IMPL
#include <xpcom/config.hpp>
#include <xpcom/detail/array_impl_templ.hpp>

#else // XPCOM_USE_TEMPLATE_ARRAY_IMPL
#include <xpcom/config.hpp>
#include <cstring> // for std::memset
#include <algorithm>
#include <xpcom/assert.hpp>
#include <xpcom/memory.hpp>
#include <xpcom/detail/array_impl.hpp>

namespace xpcom {
namespace detail {

// array_impl_base
array_impl_base::array_impl_base(array_impl_base& rhs) throw() :
    base_type(rhs)
{
    rhs.data_ = 0;
    rhs.size_ = 0;
    rhs.length_ = 0;
}

array_impl_base::array_impl_base(size_type sn, size_type n, bool init) :
    base_type()
{
    if (n > 0) {
        data_ = memory::alloc_data(sn * n);
        size_ = n;
        length_ = n;

        if (init)
            std::memset(data_, 0, sn * n);
    }
}

array_impl_base::~array_impl_base() throw()
{
    if (data_)
        memory::free_data(data_);
}

void array_impl_base::swap(array_impl_base& rhs) throw()
{
    std::swap(static_cast<base_type&>(*this), static_cast<base_type&>(rhs));
}

void array_impl_base::reset() throw()
{
    if (data_) {
        memory::free_data(data_);
        data_ = 0;
        size_ = 0;
        length_ = 0;
    }
    XPCOM_ASSERT(!data_);
    XPCOM_ASSERT(size_ == 0);
    XPCOM_ASSERT(length_ == 0);
}

void array_impl_base::release(
        void** data, size_type* size, size_type* length) throw()
{
    XPCOM_ASSERT(data);
    XPCOM_ASSERT(size != 0);
    XPCOM_ASSERT(length != 0);

    *data = data_;
    *size = size_;
    *length = length_;

    data_ = 0;
    size_ = 0;
    length_ = 0;
}

void array_impl_base::release(void** data, size_type* size) throw()
{
    XPCOM_ASSERT(data);
    XPCOM_ASSERT(size != 0);

    *data = data_;
    *size = length_;

    data_ = 0;
    size_ = 0;
    length_ = 0;
}

void array_impl_base::reserve(size_type sn, size_type n)
{
    if (n > size_) {
        data_ = memory::realloc_data(data_, sn * n);
        size_ = n;
    }
}

void array_impl_base::resize(size_type sn, size_type n, bool init)
{
    if (n < length_) {
        XPCOM_ASSERT(data_);
        length_ = n;

    } else if (n > length_) {
        reserve(sn, n);
        XPCOM_ASSERT(data_);

        if (init) {
            std::memset(static_cast<char*>(data_) + sn * length_,
                    0, sn * (n - length_));
        }

        length_ = n;
    }
}

// array_impl_base2
array_impl_base2::array_impl_base2(array_impl_base2& rhs) throw() :
    base_type(rhs)
{
    rhs.data_ = 0;
    rhs.size_ = 0;
}

array_impl_base2::array_impl_base2(size_type sn, size_type n, bool init) :
    base_type()
{
    if (n > 0) {
        data_ = memory::alloc_data(sn * n);
        size_ = n;

        if (init)
            std::memset(data_, 0, sn * n);
    }
}

array_impl_base2::~array_impl_base2() throw()
{
    if (data_)
        memory::free_data(data_);
}

void array_impl_base2::swap(array_impl_base2& rhs) throw()
{
    std::swap(static_cast<base_type&>(*this), static_cast<base_type&>(rhs));
}

void array_impl_base2::reset() throw()
{
    if (data_) {
        memory::free_data(data_);
        data_ = 0;
        size_ = 0;
    }
    XPCOM_ASSERT(!data_);
    XPCOM_ASSERT(size_ == 0);
}

void array_impl_base2::release(
        void** data, size_type* size, size_type* length) throw()
{
    XPCOM_ASSERT(data);
    XPCOM_ASSERT(size != 0);
    XPCOM_ASSERT(length != 0);

    *data = data_;
    *size = size_;
    *length = size_;

    data_ = 0;
    size_ = 0;
}

void array_impl_base2::release(void** data, size_type* size) throw()
{
    XPCOM_ASSERT(data);
    XPCOM_ASSERT(size != 0);

    *data = data_;
    *size = size_;

    data_ = 0;
    size_ = 0;
}

void array_impl_base2::reserve(size_type sn, size_type n)
{
    if (n > size_) {
        data_ = memory::realloc_data(data_, sn * n);
        //size_ = n;
    }
}

void array_impl_base2::resize(size_type sn, size_type n, bool init)
{
    if (n < size_) {
        // XXX do not realloc data here, it is just OK
        XPCOM_ASSERT(data_);
        size_ = n;

    } else if (n > size_) {
        data_ = memory::realloc_data(data_, sn * n);
        XPCOM_ASSERT(data_);

        if (init) {
            std::memset(static_cast<char*>(data_) + sn * size_,
                    0, sn * (n - size_));
        }

        size_ = n;
    }
}

} // namespace xpcom::detail
} // namespace xpcom

#endif // XPCOM_USE_TEMPLATE_ARRAY_IMPL
